/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package neuralnetwork;

import java.util.Random;

/**
 * 神经元类 神经网络的基本计算单元，支持多种激活函数
 *
 * @author 朱婧雯
 */
public class Neuron {

    public enum ActivationFunction {
        SIGMOID, TANH, RELU, LEAKY_RELU
    }

    public double value;
    public double[] weights;
    public double bias;
    public double delta;
    public double weightedSum;
    private ActivationFunction activationFunction = ActivationFunction.SIGMOID;

    public Neuron(int nextLayerSize, Random rand) {
        if (nextLayerSize < 0) {
            throw new IllegalArgumentException("下一层大小不能为负数");
        }
        if (rand == null) {
            throw new IllegalArgumentException("随机数生成器不能为null");
        }

        if (nextLayerSize > 0) {
            weights = new double[nextLayerSize];
            // Xavier初始化
            double stddev = Math.sqrt(2.0 / (nextLayerSize));
            for (int i = 0; i < nextLayerSize; i++) {
                weights[i] = rand.nextGaussian() * stddev;
            }
        } else {
            weights = new double[0];
        }

        bias = rand.nextGaussian() * 0.1;

        value = 0.0;
        delta = 0.0;
        weightedSum = 0.0;
    }

    /**
     * 多种激活函数
     *
     * @param x
     * @param func
     * @return
     */
    public double activate(double x, ActivationFunction func) {
        switch (func) {
            case SIGMOID:
                return 1.0 / (1.0 + Math.exp(-x));
            case TANH:
                return Math.tanh(x);
            case RELU:
                return Math.max(0, x);
            case LEAKY_RELU:
                return x > 0 ? x : 0.01 * x;
            default:
                return sigmoid(x);
        }
    }

    /**
     * 激活函数导数
     *
     * @param x
     * @param func
     * @return
     */
    public double activateDerivative(double x, ActivationFunction func) {
        switch (func) {
            case SIGMOID:
                double s = activate(x, func);
                return s * (1 - s);
            case TANH:
                double t = Math.tanh(x);
                return 1 - t * t;
            case RELU:
                return x > 0 ? 1 : 0;
            case LEAKY_RELU:
                return x > 0 ? 1 : 0.01;
            default:
                return sigmoidDerivative();
        }
    }

    // 保持原有方法兼容性
    public double sigmoid(double x) {
        return activate(x, ActivationFunction.SIGMOID);
    }

    public double sigmoidDerivative() {
        return activateDerivative(this.weightedSum, ActivationFunction.SIGMOID);
    }

    /**
     * 使用配置的激活函数
     *
     * @param inputs
     * @return
     */
    public double activate(double[] inputs) {
        if (inputs == null) {
            throw new IllegalArgumentException("输入数据不能为null");
        }
        if (inputs.length != weights.length) {
            throw new IllegalArgumentException(
                    String.format("输入数据维度不匹配: 期望%d, 实际%d", weights.length, inputs.length));
        }

        double sum = bias;
        for (int i = 0; i < weights.length; i++) {
            sum += inputs[i] * weights[i];
        }
        this.weightedSum = sum;
        this.value = activate(sum, activationFunction);
        return this.value;
    }

    /**
     * 获取当前激活函数的导数
     *
     * @return
     */
    public double getActivationDerivative() {
        return activateDerivative(this.weightedSum, activationFunction);
    }

    public void setWeightsAndBias(double[] newWeights, double newBias) {
        if (newWeights == null) {
            throw new IllegalArgumentException("新权重数组不能为null");
        }

        if (newWeights.length == weights.length) {
            System.arraycopy(newWeights, 0, weights, 0, weights.length);
        } else {
            throw new IllegalArgumentException(
                    String.format("权重数组长度不匹配: 期望%d, 实际%d", weights.length, newWeights.length));
        }
        bias = newBias;
    }

    @Override
    public String toString() {
        return String.format(
                "Neuron[输出=%.4f, 权重数=%d, 偏置=%.4f, delta=%.6f, 激活函数=%s]",
                value, weights.length, bias, delta, activationFunction
        );
    }

    public double[] getWeightsCopy() {
        return weights.clone();
    }

    public double[] getWeights() {
        return weights;
    }

    public double getBias() {
        return bias;
    }

    public double getValue() {
        return value;
    }

    public double getDelta() {
        return delta;
    }

    public void setDelta(double delta) {
        this.delta = delta;
    }

    public double getWeightedSum() {
        return weightedSum;
    }

    public ActivationFunction getActivationFunction() {
        return activationFunction;
    }

    public void setActivationFunction(ActivationFunction activationFunction) {
        this.activationFunction = activationFunction;
    }
}
